#include "precom.h"
#include "HalfSphereGenerator.h"

void HalfSphereGenerator::CreateHalfOctagon(float a_radius)
{
	Tria tr;
	tr.m_points[0].set(0.f, 0.f, -a_radius);
	tr.m_normals[0].set(0.f, 0.f, -1.f);
	tr.m_points[1].set(0.f, -a_radius, 0.f);
	tr.m_normals[1].set(0.f, -1.f, 0.f);
	tr.m_points[2].set(-a_radius, 0.f, 0.f);
	tr.m_normals[2].set(-1.f, 0.f, 0.f);
	m_sphereTriangles.push_back(tr);

	tr.m_points[0].set(0.f, 0.f, -a_radius);
	tr.m_normals[0].set(0.f, 0.f, -1.f);
	tr.m_points[1].set(0.f, -a_radius, 0.f);
	tr.m_normals[1].set(0.f, -1.f, 0.f);
	tr.m_points[2].set(a_radius, 0.f, 0.f);
	tr.m_normals[2].set(1.f, 0.f, 0.f);
	m_sphereTriangles.push_back(tr);

	tr.m_points[0].set(0.f, 0.f, -a_radius);
	tr.m_normals[0].set(0.f, 0.f, -1.f);
	tr.m_points[1].set(0.f, a_radius, 0.f);
	tr.m_normals[1].set(0.f, 1.f, 0.f);
	tr.m_points[2].set(-a_radius, 0.f, 0.f);
	tr.m_normals[2].set(-1.f, 0.f, 0.f);
	m_sphereTriangles.push_back(tr);

	tr.m_points[0].set(0.f, 0.f, -a_radius);
	tr.m_normals[0].set(0.f, 0.f, -1.f);
	tr.m_points[1].set(0.f, a_radius, 0.f);
	tr.m_normals[1].set(0.f, 1.f, 0.f);
	tr.m_points[2].set(a_radius, 0.f, 0.f);
	tr.m_normals[2].set(1.f, 0.f, 0.f);
	m_sphereTriangles.push_back(tr);
}

fmath::vector3 HalfSphereGenerator::DivLine(fmath::vector3& v1, fmath::vector3& v2) const
{
	fmath::vector3 p;
	p.x = (v1.x + v2.x) * 0.5f;
	p.y = (v1.y + v2.y) * 0.5f;
	p.z = (v1.z + v2.z) * 0.5f;
	return p;
}

void HalfSphereGenerator::DivadeTriangle(Tria& a_tr)
{
	fmath::vector3 mp1, mp2, mp3;
	Tria tran[4];
	tran[0].m_points[0] = a_tr.m_points[0];
	tran[0].m_normals[0] = a_tr.m_normals[0];
	tran[1].m_points[1] = a_tr.m_points[1];
	tran[1].m_normals[1] = a_tr.m_normals[1];
	tran[2].m_points[2] = a_tr.m_points[2];
	tran[2].m_normals[2] = a_tr.m_normals[2];
	mp1 = DivLine(a_tr.m_points[0], a_tr.m_points[1]);
	mp2 = DivLine(a_tr.m_points[1], a_tr.m_points[2]);
	mp3 = DivLine(a_tr.m_points[0], a_tr.m_points[2]);
	mp1.normalize();
	mp2.normalize();
	mp3.normalize();
	tran[0].m_normals[1] = tran[1].m_normals[0] = 
		tran[3].m_normals[1] = mp1;
	tran[0].m_normals[2] = tran[2].m_normals[0] = 
		tran[3].m_normals[2] = mp3;
	tran[1].m_normals[2] = tran[2].m_normals[1] = 
		tran[3].m_normals[0] = mp2;
	mp1 *= m_radius;
	mp2 *= m_radius;
	mp3 *= m_radius;
	tran[0].m_points[1] = tran[1].m_points[0] = 
		tran[3].m_points[1] = mp1;
	tran[0].m_points[2] = tran[2].m_points[0] = 
		tran[3].m_points[2] = mp3;
	tran[1].m_points[2] = tran[2].m_points[1] = 
		tran[3].m_points[0] = mp2;
	m_sphereTriangles.push_back(tran[0]);
	m_sphereTriangles.push_back(tran[1]);
	m_sphereTriangles.push_back(tran[2]);
	m_sphereTriangles.push_back(tran[3]);
}

void HalfSphereGenerator::Generating(float a_radius, float a_height)
{
	Tria tr;
	size_t elem;
	m_radius = a_radius;
	m_height = a_height;

	size_t count = static_cast<size_t>(ceilf(log(m_radius * 20.0f + 10.0f)));
	m_sphereTriangles.clear();
	m_boundList.clear();
	CreateHalfOctagon(a_radius);
	
	for(size_t i=0; i<count; ++i) {
		elem = m_sphereTriangles.size();
		for(size_t k=0; k<elem; ++k) {
			tr = m_sphereTriangles.front();
			m_sphereTriangles.pop_front();
			DivadeTriangle(tr);
		}
	}
}

GLuint HalfSphereGenerator::CompileModel()
{
	GLuint list = 0;
	float h1 = 0.f, h2 = m_height;
	fmath::vector3 n1;
	std::list<Tria>::iterator iter = m_sphereTriangles.begin();
	std::list<fmath::vector3>::iterator iterPoint;
	list = glGenLists(1);
	if(list == 0) return 0;
	glNewList(list, GL_COMPILE);
		glBegin(GL_TRIANGLES);
		while( iter != m_sphereTriangles.end() ) {
			glNormal3f(iter->m_normals[0].x, iter->m_normals[0].y, iter->m_normals[0].z);
			glVertex3f(iter->m_points[0].x, iter->m_points[0].y, iter->m_points[0].z);
			glNormal3f(iter->m_normals[1].x, iter->m_normals[1].y, iter->m_normals[1].z);
			glVertex3f(iter->m_points[1].x, iter->m_points[1].y, iter->m_points[1].z);
			glNormal3f(iter->m_normals[2].x, iter->m_normals[2].y, iter->m_normals[2].z);
			glVertex3f(iter->m_points[2].x, iter->m_points[2].y, iter->m_points[2].z);
			if(iter->m_points[1].z > -EPS) {
				m_boundList.push_back(iter->m_points[1]);
				m_boundList.push_back(iter->m_points[2]);
			}
			++iter;
		}		
		glEnd();
		iterPoint = m_boundList.begin();
		glBegin(GL_QUADS);
		while(iterPoint != m_boundList.end()) {
			n1 = *iterPoint;
			n1.normalize();
			glNormal3f(n1.x, n1.y, n1.z);
			glVertex3f(iterPoint->x, iterPoint->y, iterPoint->z + h1);
			glVertex3f(iterPoint->x, iterPoint->y, iterPoint->z + h2);
			++iterPoint;
			h1 = -h1 + m_height;
			h2 = -h2 + m_height;
		}
		glEnd();
	glEndList();
	return list;
}

GLuint HalfSphereGenerator::GenerateSphere(float a_radius, float a_height)
{
	GLuint compiledTool;
	Generating(a_radius, a_height);
	compiledTool = CompileModel();
	m_sphereTriangles.clear();
	m_boundList.clear();
	return compiledTool;
}